package es.manuel.maa.applet;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.atomic.AtomicInteger;

import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class AppletGlasspane extends JComponent implements ActionListener,
		KeyEventDispatcher, PropertyChangeListener {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -4838638376952529102L;
	/**
	 * Frame de la animaci&oacute;n.
	 */
	protected int frame = -1; // animation dialog index
	/**
	 * N&uacute;mero de barras de la estrella.
	 */
	protected int nBars = 8;
	/**
	 * Anchura de la barra.
	 */
	protected float barWidth = 6;

	/**
	 * Largo de la barra
	 */
	protected float barHeight = 16;

	/**
	 * Cuantas barras se arrastran.
	 */
	protected int trailLength = 4;
	/**
	 * Sombreado del gris.
	 */
	protected float barGray = 200f;

	/**
	 * Color del cristal.
	 */
	protected float glassColor = 120f;

	/**
	 * Color del cristal.
	 */
	protected float alphaColor = 128f;

	/**
	 * Tiempo de refresco de la animaci&oacute;n en milisegundos.
	 */
	private Timer timer;

	/**
	 * Progreso.
	 */
	private final AtomicInteger progress;

	/**
	 * Applet.
	 */
	private final ConfigurableApplet applet;

	/**
	 * Constructor.
	 */
	public AppletGlasspane(ConfigurableApplet applet) {
		this.applet = applet;
		this.applet.setGlassPane(this);
		this.initComponents();
		this.progress = new AtomicInteger(0);
	}

	/**
	 * Inicializa la interfaz gr&aacute;fica del panel.
	 */
	protected void initComponents() {
		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		MouseAdapter mouseAdapter = new MouseAdapter();
		addMouseMotionListener(mouseAdapter);
		addMouseListener(mouseAdapter);
		addMouseWheelListener(mouseAdapter);
		timer = new Timer(65, this);
	}

	/**
	 * Gestiona la acci&oacute;n del timer de la animaci&oacute;n incrementando
	 * el frame a visualizar.
	 * 
	 * @param actionEvent
	 *            La acci&oacute;n del timer de la animaci&oacute;n (es
	 *            ignorado).
	 */
	public void actionPerformed(ActionEvent actionEvent) {
		++frame;
		repaint();
	}

	/**
	 * Bloquea la aplicaci&oacute;n.
	 */
	public void blockApplication() {
		if (!isVisible() && !this.applet.getActionQueue().isEmpty()) {
			this.progress.set(0);
			setVisible(true);
			requestFocusInWindow();
			KeyboardFocusManager.getCurrentKeyboardFocusManager()
					.addKeyEventDispatcher(this);
			timer.start();
			repaint();
		}
	}

	/**
	 * Desbloquea la aplicaci&oacute;n parando la animaci&oacute;n.
	 */
	public void unblockApplication() {
		this.progress.set(100);
		if (this.applet.getActionQueue().isEmpty()) {
			setVisible(false);
			timer.stop();
		}
	}

	/**
	 * Desbloquea el teclado.
	 */
	public void unblockKeyboard() {
		KeyboardFocusManager.getCurrentKeyboardFocusManager()
				.removeKeyEventDispatcher(this);
	}

	/**
	 * Bloquea el teclado.
	 */
	public void blockKeyboard() {
		// si ya est&aacute; visible entonces lo ignoramos ya que alguien lo
		// quitar&aacute;
		if (!isVisible()) {
			KeyboardFocusManager.getCurrentKeyboardFocusManager()
					.addKeyEventDispatcher(this);
		}
	}

	/**
	 * Bloquea los eventos de teclado cuando est&aacute; visible.
	 * 
	 * @param e
	 *            El evento del teclado que se ha generado.
	 * @return true si debe procesar el evento (si no est&aacute; visible) false
	 *         en otro caso (si est&aacute; visible).
	 */
	public boolean dispatchKeyEvent(KeyEvent e) {
		return ((JComponent) e.getSource()).getRootPane().isVisible();
	}

	/**
	 * Pinta este componente.
	 * 
	 * @param g
	 *            El contexto gr&aacute;fico donde pintarlo.
	 */
	@Override
	protected void paintComponent(Graphics g) {
		FontMetrics metrics = g.getFontMetrics();
		Rectangle2D rect = metrics.getStringBounds("100%", g);
		float innerRadius = (float) (rect.getWidth() / 2) + 3;
		float outerRadius = innerRadius + barHeight;

		g.setColor(new Color((int) glassColor, (int) glassColor,
				(int) glassColor, (int) alphaColor));
		g.fillRect(0, 0, getWidth(), getHeight());
		RoundRectangle2D bar = new RoundRectangle2D.Float(innerRadius,
				-barWidth / 2, outerRadius, barWidth, barWidth, barWidth);
		double angle = Math.PI * 2.0 / (double) nBars;
		Graphics2D g2d = (Graphics2D) g;
		g2d.translate(getWidth() / 2, getHeight() / 2);
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		for (int i = 0; i < nBars; i++) {
			Color barColor = new Color((int) barGray, (int) barGray,
					(int) barGray);
			if (frame != -1) {
				for (int t = 0; t < trailLength; t++) {
					if (i == ((frame - t + nBars) % nBars)) {
						float tlf = (float) trailLength;
						float pct = 1.0f - ((tlf - t) / tlf);
						int gray = (int) ((barGray - (pct * barGray)) + 0.5f);
						barColor = new Color(gray, gray, gray);
					}
				}
			}
			g2d.setColor(barColor);
			g2d.fill(bar);
			g2d.rotate(angle);
		}

		Color textColor = new Color((int) barGray, (int) barGray, (int) barGray);
		g2d.setColor(textColor);
		String text = String.format("%s%s", progress.get(), "%");
		rect = metrics.getStringBounds(text, g);
		g2d.drawString(text, (int) (0 - (rect.getWidth() / 2)),
				(int) (0 + (rect.getHeight() / 4)));
	}

	private class MouseAdapter implements MouseListener, MouseWheelListener,
			MouseMotionListener {
		/**
		 * {@inheritDoc}
		 */
		public void mouseClicked(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mousePressed(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseReleased(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseEntered(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseExited(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseWheelMoved(MouseWheelEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseDragged(MouseEvent e) {
		}

		/**
		 * {@inheritDoc}
		 */
		public void mouseMoved(MouseEvent e) {
		}
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress".equals(evt.getPropertyName())) {
			this.progress.set((Integer) evt.getNewValue());
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					repaint();
				}
			});
		}

	}

}